import * as BL from '../index';


/**
 * 资源路径类型
 */
type UrlInfo = string | BL.IBufferLoadInfo;

/**
 * 资源任务信息类型
 * @param GLTF_MESH 网格模型（glb）
 * @param TEXTURE 纹理贴图（jpg，png）
 * @param CUBE_TEXTURE 立体纹理贴图（盒型，jpg，png）
 * @param EQUI_RECTANGULAR_CUBE_TEXTURE 等矩形立方体纹理（球形，jpg，png）
 * @param HDR_CUBE_TEXTURE hdr立体纹理贴图（hdr）
 * @param AUDIO 音频（mp3）
 * @param VIDEO 视频（mp4）
 * @param DATA 数据（json、xml等）
 */
enum TASK_INFO_TYPES {
    GLTF_MESH,
    TEXTURE,
    CUBE_TEXTURE,
    EQUI_RECTANGULAR_CUBE_TEXTURE,
    HDR_CUBE_TEXTURE,
    AUDIO,
    VIDEO,
    DATA,
};

/**
 * 资源任务联合类型
 */
type AssetTask = BL.MeshAssetTask | BL.TextureAssetTask | BL.CubeTextureAssetTask | BL.EquiRectangularCubeTextureAssetTask |
    BL.HDRCubeTextureAssetTask | BL.BinaryFileAssetTask;


/**
 * 资源加载任务信息抽象类（用于资源加载器加载资源、并初始化资源对象）
 * @param info 资源路径信息
 */
abstract class AbstractAssetTaskInfo {

    /**
     * 类型标识
     */
    public abstract type: number;

    /**
     * 资源加载任务
     */
    public task!: AssetTask | null;

    /**
     * 原始地址
     */
    public rawUrl!: string;

    /**
     * 是否加载完成
     */
    public loaded: boolean = false;

    /**
     * 地址信息
     */
    protected info: UrlInfo;

    /**
     * 是否是地址
     */
    protected isUrl: boolean;

    constructor(info: UrlInfo) {
        this.info = info;
        this.isUrl = typeof info === 'string';
    }

    /**
     * 添加任务到资源管理器
     * @param manager 资源管理器
     */
    public abstract setTask(manager: BL.AssetsManager): AssetTask | null;

    /**
     * 获取名称（即原始地址字符串）
     * @param info 地址信息
     * @returns 地址字符串
     */
    protected getName(info: UrlInfo): string {
        return this.isUrl ? info as string : (info as BL.IBufferLoadInfo).url;
    }

    /**
     * 获取地址字符串
     * @param info 地址信息
     * @returns 地址字符串
     */
    protected getUrl(info?: UrlInfo): string {
        return this.isUrl ? info as string : (info as BL.IBufferLoadInfo).blobUrl;
    }

    /**
     * 比较资源是否相同
     * @param other 其他资源信息
     * @returns 是否相同
     */
    public compare(other: AbstractAssetTaskInfo): boolean {
        const uthis = this.isUrl ? this.info as string : (this.info as BL.IBufferLoadInfo).blobUrl;
        const uother = other.isUrl ? other.info as string : (other.info as BL.IBufferLoadInfo).blobUrl;
        return uthis === uother;
    }

    public dispose(): void {
        this.task = null;
    }
}

/**
 * gltf模型
 * @param info 资源路径信息
 */
class GLTFMeshTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.GLTF_MESH;

    /**
     * 加载的动画组
     */
    public loadedAnimationGroups?: Array<BL.AnimationGroup>;

    /**
     * 加载的网格
     */
    public loadedMeshes?: Array<BL.AbstractMesh>;

    /**
     * 加载的粒子系统
     */
    public loadedParticleSystems?: Array<BL.IParticleSystem>;

    /**
     * 加载的骨骼
     */
    public loadedSkeletons?: Array<BL.Skeleton>;

    /**
     * 加载的节点对象
     */
    public loadedTransformNodes?: Array<BL.TransformNode>;

    public setTask(manager: BL.AssetsManager): BL.MeshAssetTask {
        const info = this.info;
        this.rawUrl = this.getName(info);
        const url = this.getUrl(info);
        this.task = manager.addMeshTask(this.rawUrl, '', url, '', '.glb');
        return this.task;
    }

    public dispose(): void {
        super.dispose();
        if (this.loadedAnimationGroups) this.loadedAnimationGroups.forEach(x => x.dispose());
        if (this.loadedTransformNodes) this.loadedTransformNodes.forEach(x => x.dispose());
        if (this.loadedMeshes) this.loadedMeshes.forEach(x => x.dispose());
        if (this.loadedSkeletons) this.loadedSkeletons.forEach(x => x.dispose());
        if (this.loadedParticleSystems) this.loadedParticleSystems.forEach(x => x.dispose());
    }
}

/**
 * 纹理
 * @param info 资源路径信息
 */
class TextureTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.TEXTURE;

    /**
     * 加载的纹理贴图
     */
    public texture?: BL.Texture;

    public setTask(manager: BL.AssetsManager): BL.TextureAssetTask {
        const info = this.info;
        this.rawUrl = this.getName(info);
        const url = this.getUrl(info);
        this.task = manager.addTextureTask(this.rawUrl, url)
        return this.task;
    }

    public dispose(): void {
        super.dispose();
        if (this.texture) this.texture.dispose();
    }
}

/**
 * 立方体纹理
 * @param px 正x方向纹理的地址信息
 * @param py 正y方向纹理的地址信息
 * @param pz 正z方向纹理的地址信息
 * @param nx 负x方向纹理的地址信息
 * @param ny 负y方向纹理的地址信息
 * @param nz 负z方向纹理的地址信息
 * @param behalfIndex 代表索引（该索引地址将代表整个立方体纹理，默认为0）
 */
class CubeTextureTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.CUBE_TEXTURE;

    /**
     * 加载的纹理贴图
     */
    public texture?: BL.CubeTexture;

    private _infos: Array<UrlInfo>;
    private _behalfIndex: number;
    constructor(px: UrlInfo, py: UrlInfo, pz: UrlInfo, nx: UrlInfo, ny: UrlInfo, nz: UrlInfo, behalfIndex: number = 0) {
        super('');
        this._infos = [px, py, pz, nx, ny, nz];
        this._behalfIndex = Math.max(Math.min(this._infos.length - 1, behalfIndex), 0);
    }
    public setTask(manager: BL.AssetsManager): BL.CubeTextureAssetTask {
        const infos = this._infos;
        const fInfo = infos[this._behalfIndex];
        const isUrl = typeof fInfo === 'string';
        this.rawUrl = isUrl ? fInfo : fInfo.url;
        this.task = manager.addCubeTextureTask(this.rawUrl, this.rawUrl, undefined, false,
            [this.getUrl(infos[0]), this.getUrl(infos[1]), this.getUrl(infos[2]), this.getUrl(infos[3]), this.getUrl(infos[4]), this.getUrl(infos[5])],
            false);
        return this.task;
    }
    protected getUrl(info: UrlInfo): string {
        this.isUrl = typeof info === 'string';
        return this.isUrl ? info as string : (info as BL.IBufferLoadInfo).blobUrl;
    }

    public dispose(): void {
        super.dispose();
        if (this.texture) this.texture.dispose();
    }
}

/**
 * 球面投影立方体纹理
 * @param info 地址信息
 * @param size 纹理尺寸
 */
class EquiRectangularCubeTextureTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.EQUI_RECTANGULAR_CUBE_TEXTURE;

    /**
     * 加载的纹理
     */
    public texture?: BL.EquiRectangularCubeTexture;

    protected size: number;
    constructor(info: UrlInfo, size: number = 512) {
        super(info);
        this.size = size;
    }
    public setTask(manager: BL.AssetsManager): BL.EquiRectangularCubeTextureAssetTask {
        const info = this.info;
        this.rawUrl = this.getName(info);
        const url = this.getUrl(info);
        this.task = manager.addEquiRectangularCubeTextureAssetTask(this.rawUrl, url, this.size, false, undefined);
        return this.task;
    }

    public dispose(): void {
        super.dispose();
        if (this.texture) this.texture.dispose();
    }
}

/**
 * HDR立方体纹理
 * @param info 地址信息
 * @param size 纹理尺寸
 */
class HDRCubeTextureTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.HDR_CUBE_TEXTURE;

    /**
     * 加载的纹理
     */
    public texture?: BL.HDRCubeTexture;

    protected size: number;
    constructor(info: UrlInfo, size: number = 512) {
        super(info);
        this.size = size;
    }
    public setTask(manager: BL.AssetsManager): BL.HDRCubeTextureAssetTask {
        const info = this.info;
        this.rawUrl = this.getName(info);
        const url = this.getUrl(info);
        this.task = manager.addHDRCubeTextureTask(this.rawUrl, url, this.size, false, true, false);
        return this.task;
    }

    public dispose(): void {
        super.dispose();
        if (this.texture) this.texture.dispose();
    }
}

/**
 * 音频
 * @param info 地址信息
 */
class AudioTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.AUDIO;

    /**
     * 加载的音频
     */
    public sound?: BL.AbstractSound;

    public setTask(manager: BL.AssetsManager): BL.BinaryFileAssetTask {
        const info = this.info;
        this.rawUrl = this.getName(info);
        const url = this.getUrl(info);
        this.task = manager.addBinaryFileTask(this.rawUrl, url);
        return this.task;
    }

    public dispose(): void {
        super.dispose();
        if (this.sound) this.sound.dispose();
    }
}

/**
 * 视频纹理
 * @param info 地址信息
 */
class VideoTextureTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.VIDEO;

    /**
     * 加载的视频纹理
     */
    public texture?: BL.VideoTexture;

    /**
     * 视频纹理的数据地址
     */
    public dataUrl?: string;

    public setTask(): null {
        const info = this.info;
        this.rawUrl = this.getName(info);
        this.task = null;
        this.dataUrl = this.getUrl(info);
        return this.task;
    }

    public dispose(): void {
        super.dispose();
        if (this.texture) this.texture.dispose();
    }
}

/**
 * 二进制数据（扩展为具体的格式数据）
 * @param info 地址信息
 */
class DataTaskInfo extends AbstractAssetTaskInfo {
    public type: number = TASK_INFO_TYPES.DATA;

    /**
     * 加载的数据
     */
    public data?: ArrayBuffer;

    public setTask(manager: BL.AssetsManager): BL.BinaryFileAssetTask {
        const info = this.info;
        this.rawUrl = this.getName(info);
        const url = this.getUrl(info);
        this.task = manager.addBinaryFileTask(this.rawUrl, url);
        return this.task;
    }

}

export {
    GLTFMeshTaskInfo,
    TextureTaskInfo,
    CubeTextureTaskInfo,
    EquiRectangularCubeTextureTaskInfo,
    HDRCubeTextureTaskInfo,
    AudioTaskInfo,
    VideoTextureTaskInfo,
    DataTaskInfo,
    TASK_INFO_TYPES,
    AbstractAssetTaskInfo
};